﻿using Consul;
using Developer.Core.FrameWork.RegisterCenter.Model;
using Developer.Core.FrameWork.RegisterCenter.Options;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Net;

namespace Developer.Core.FrameWork.RegisterCenter.Service
{
    /// <summary>
    /// 抽象服务发现，缓存层
    /// </summary>
    public abstract class AbstractServiceDiscovery : IServiceDiscovery
    {
        private readonly Dictionary<string, List<ServiceNode>> CacheConsulResult = new Dictionary<string, List<ServiceNode>>();
        protected readonly ServiceDiscoveryOptions serviceDiscoveryOptions;

        public AbstractServiceDiscovery(IOptions<ServiceDiscoveryOptions> options)
        {
            // 1、服务发现配置选项
            this.serviceDiscoveryOptions = options.Value;

            // 2、创建consul客户端连接
            var consulClient = new ConsulClient(configuration =>
            {
                configuration.Address = new Uri(this.serviceDiscoveryOptions.DiscoveryAddress);
            });

            // 3、consul服务查询
            var queryResult = consulClient.Catalog.Services().Result;

            if (!queryResult.StatusCode.Equals(HttpStatusCode.OK))
                throw new ArgumentNullException($"consul服务连接失败");

            // 4、获取服务下的所有实例
            foreach (var item in queryResult.Response)
            {
                QueryResult<CatalogService[]> result = consulClient.Catalog.Service(item.Key).Result;

                if (!queryResult.StatusCode.Equals(HttpStatusCode.OK))
                    throw new ArgumentNullException($"consul连接失败");

                var list = new List<ServiceNode>();
                foreach (var service in result.Response)
                    list.Add(new ServiceNode
                    {
                        Url = service.Address + ":" + service.ServicePort,
                        ServiceID = service.ServiceID,
                        ServiceAddress = service.ServiceAddress,
                        ServiceName = service.ServiceName,
                        ServicePort = service.ServicePort,
                        ServiceTags = service.ServiceTags
                    });

                CacheConsulResult.Add(item.Key, list);
            }
        }

        public List<ServiceNode> Discovery(string serviceName)
        {
            if (this.CacheConsulResult.ContainsKey(serviceName))
                return this.CacheConsulResult[serviceName];
            else
            {
                CatalogService[] queryResult = RemoteDiscovery(serviceName);

                var list = new List<ServiceNode>();

                foreach (var service in queryResult)
                    list.Add(new ServiceNode { 
                        Url = service.Address + ":" + service.ServicePort,
                        ServiceID = service.ServiceID,
                        ServiceAddress = service.ServiceAddress,
                        ServiceName = service.ServiceName,
                        ServicePort = service.ServicePort,
                        ServiceTags = service.ServiceTags
                    });

                this.CacheConsulResult.Add(serviceName, list);

                return list;
            }
        }

        protected abstract CatalogService[] RemoteDiscovery(string serviceName);
    }
}
